/**
 * Copyright (c) 2006-2024 LOVE Development Team
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 **/

#include "Keyboard.h"
#include "window/Window.h"
#include "common/config.h"

namespace love
{
namespace keyboard
{
namespace sdl
{

static SDL_Window *getSDLWindow()
{
	auto window = Module::getInstance<window::Window>(Module::M_WINDOW);
	if (window)
		return (SDL_Window *) window->getHandle();
	return nullptr;
}

Keyboard::Keyboard()
	: love::keyboard::Keyboard("love.keyboard.sdl")
	, key_repeat(false)
{
}

void Keyboard::setKeyRepeat(bool enable)
{
	key_repeat = enable;
}

bool Keyboard::hasKeyRepeat() const
{
	return key_repeat;
}

bool Keyboard::isDown(const std::vector<Key> &keylist) const
{
	const bool *state = SDL_GetKeyboardState(nullptr);

	for (Key key : keylist)
	{
		SDL_Keycode sdlkey = SDLK_UNKNOWN;
		if (getConstant(key, sdlkey))
		{
			SDL_Scancode scancode = SDL_GetScancodeFromKey(sdlkey, nullptr);
			if (state[scancode])
				return true;
		}
	}

	return false;
}

bool Keyboard::isScancodeDown(const std::vector<Scancode> &scancodelist) const
{
	const bool *state = SDL_GetKeyboardState(nullptr);

	for (Scancode scancode : scancodelist)
	{
		SDL_Scancode sdlcode = SDL_SCANCODE_UNKNOWN;

		if (scancodes.find(scancode, sdlcode) && state[sdlcode])
			return true;
	}

	return false;
}

bool Keyboard::isModifierActive(ModifierKey key) const
{
	int modstate = SDL_GetModState();

	switch (key)
	{
	case MODKEY_NUMLOCK:
		return (modstate & SDL_KMOD_NUM) != 0;
	case MODKEY_CAPSLOCK:
		return (modstate & SDL_KMOD_CAPS) != 0;
	case MODKEY_SCROLLLOCK:
		return (modstate & SDL_KMOD_SCROLL) != 0;
	case MODKEY_MODE:
		return (modstate & SDL_KMOD_MODE) != 0;
	default:
		break;
	}

	return false;
}

Keyboard::Key Keyboard::getKeyFromScancode(Scancode scancode) const
{
	SDL_Scancode sdlscancode = SDL_SCANCODE_UNKNOWN;
	scancodes.find(scancode, sdlscancode);

	SDL_Keycode sdlkey = SDL_GetKeyFromScancode(sdlscancode, SDL_KMOD_NONE, false);

	Key key = KEY_UNKNOWN;
	getConstant(sdlkey, key);
	return key;
}

Keyboard::Scancode Keyboard::getScancodeFromKey(Key key) const
{
	Scancode scancode = SCANCODE_UNKNOWN;

	SDL_Keycode sdlkey = SDLK_UNKNOWN;
	if (getConstant(key, sdlkey))
	{
		SDL_Scancode sdlscancode = SDL_GetScancodeFromKey(sdlkey, nullptr);
		scancodes.find(sdlscancode, scancode);
	}

	return scancode;
}

void Keyboard::setTextInput(bool enable)
{
	SDL_Window *window = getSDLWindow();
	if (window == nullptr)
		return;
	if (enable)
		SDL_StartTextInput(window);
	else
		SDL_StopTextInput(window);
}

void Keyboard::setTextInput(bool enable, double x, double y, double w, double h)
{
	// SDL_SetTextInputRect expects coordinates in window-space but setTextInput
	// takes pixels, so we should convert.
	auto window = Module::getInstance<window::Window>(M_WINDOW);
	if (window)
	{
		window->DPIToWindowCoords(&x, &y);
		window->DPIToWindowCoords(&w, &h);
	}

	SDL_Rect rect = {(int) x, (int) y, (int) w, (int) h};

	SDL_Window *sdlwindow = getSDLWindow();
	if (sdlwindow != nullptr)
		SDL_SetTextInputArea(sdlwindow, &rect, 0);

	setTextInput(enable);
}

bool Keyboard::hasTextInput() const
{
	SDL_Window *window = getSDLWindow();
	if (window == nullptr)
		return false;
	return SDL_TextInputActive(window);
}

bool Keyboard::hasScreenKeyboard() const
{
	return SDL_HasScreenKeyboardSupport();
}

bool Keyboard::getConstant(Key in, SDL_Keycode &out)
{
	if (in & KEY_SCANCODE_MASK)
	{
		auto it = keyToSDLKey.find(in);
		if (it != keyToSDLKey.end())
		{
			out = it->second;
			return true;
		}
	}
	else
	{
		// All other keys use the same value as their ASCII character
		// representation in SDL and in love.
		out = (SDL_Keycode)in;
		return true;
	}

	return false;
}

bool Keyboard::getConstant(SDL_Keycode in, Key &out)
{
#ifdef LOVE_ANDROID
	// TODO: Can this be done more cleanly?
	if (in == SDLK_AC_BACK)
	{
		out = KEY_ESCAPE;
		return true;
	}
#endif

	if (in & SDLK_SCANCODE_MASK)
	{
		if (sdlKeyToKey.empty())
		{
			for (const auto& kvp : keyToSDLKey)
				sdlKeyToKey[kvp.second] = kvp.first;
		}

		auto it = sdlKeyToKey.find(in);
		if (it != sdlKeyToKey.end())
		{
			out = it->second;
			return true;
		}
	}
	else
	{
		// All other keys use the same value as their ASCII character
		// representation in SDL and in love.
		out = (Key)in;
		return true;
	}

	return false;
}

bool Keyboard::getConstant(Scancode in, SDL_Scancode &out)
{
	return scancodes.find(in, out);
}

bool Keyboard::getConstant(SDL_Scancode in, Scancode &out)
{
	return scancodes.find(in, out);
}

std::map<Keyboard::Key, SDL_Keycode> Keyboard::keyToSDLKey =
{
	// ASCII characters don't need to go here since they can be directly
	// converted.

	{ KEY_UNKNOWN, SDLK_UNKNOWN },

	{ KEY_CAPSLOCK, SDLK_CAPSLOCK },

	{ KEY_F1, SDLK_F1 },
	{ KEY_F2, SDLK_F2 },
	{ KEY_F3, SDLK_F3 },
	{ KEY_F4, SDLK_F4 },
	{ KEY_F5, SDLK_F5 },
	{ KEY_F6, SDLK_F6 },
	{ KEY_F7, SDLK_F7 },
	{ KEY_F8, SDLK_F8 },
	{ KEY_F9, SDLK_F9 },
	{ KEY_F10, SDLK_F10 },
	{ KEY_F11, SDLK_F11 },
	{ KEY_F12, SDLK_F12 },

	{ KEY_PRINTSCREEN, SDLK_PRINTSCREEN },
	{ KEY_SCROLLLOCK, SDLK_SCROLLLOCK },
	{ KEY_PAUSE, SDLK_PAUSE },
	{ KEY_INSERT, SDLK_INSERT },
	{ KEY_HOME, SDLK_HOME },
	{ KEY_PAGEUP, SDLK_PAGEUP },
	{ KEY_DELETE, SDLK_DELETE },
	{ KEY_END, SDLK_END },
	{ KEY_PAGEDOWN, SDLK_PAGEDOWN },
	{ KEY_RIGHT, SDLK_RIGHT },
	{ KEY_LEFT, SDLK_LEFT },
	{ KEY_DOWN, SDLK_DOWN },
	{ KEY_UP, SDLK_UP },

	{ KEY_NUMLOCKCLEAR, SDLK_NUMLOCKCLEAR },
	{ KEY_KP_DIVIDE, SDLK_KP_DIVIDE },
	{ KEY_KP_MULTIPLY, SDLK_KP_MULTIPLY },
	{ KEY_KP_MINUS, SDLK_KP_MINUS },
	{ KEY_KP_PLUS, SDLK_KP_PLUS },
	{ KEY_KP_ENTER, SDLK_KP_ENTER },
	{ KEY_KP_0, SDLK_KP_0 },
	{ KEY_KP_1, SDLK_KP_1 },
	{ KEY_KP_2, SDLK_KP_2 },
	{ KEY_KP_3, SDLK_KP_3 },
	{ KEY_KP_4, SDLK_KP_4 },
	{ KEY_KP_5, SDLK_KP_5 },
	{ KEY_KP_6, SDLK_KP_6 },
	{ KEY_KP_7, SDLK_KP_7 },
	{ KEY_KP_8, SDLK_KP_8 },
	{ KEY_KP_9, SDLK_KP_9 },
	{ KEY_KP_PERIOD, SDLK_KP_PERIOD },
	{ KEY_KP_COMMA, SDLK_KP_COMMA },
	{ KEY_KP_EQUALS, SDLK_KP_EQUALS },

	{ KEY_APPLICATION, SDLK_APPLICATION },
	{ KEY_POWER, SDLK_POWER },
	{ KEY_F13, SDLK_F13 },
	{ KEY_F14, SDLK_F14 },
	{ KEY_F15, SDLK_F15 },
	{ KEY_F16, SDLK_F16 },
	{ KEY_F17, SDLK_F17 },
	{ KEY_F18, SDLK_F18 },
	{ KEY_F19, SDLK_F19 },
	{ KEY_F20, SDLK_F20 },
	{ KEY_F21, SDLK_F21 },
	{ KEY_F22, SDLK_F22 },
	{ KEY_F23, SDLK_F23 },
	{ KEY_F24, SDLK_F24 },
	{ KEY_EXECUTE, SDLK_EXECUTE },
	{ KEY_HELP, SDLK_HELP },
	{ KEY_MENU, SDLK_MENU },
	{ KEY_SELECT, SDLK_SELECT },
	{ KEY_STOP, SDLK_STOP },
	{ KEY_AGAIN, SDLK_AGAIN },
	{ KEY_UNDO, SDLK_UNDO },
	{ KEY_CUT, SDLK_CUT },
	{ KEY_COPY, SDLK_COPY },
	{ KEY_PASTE, SDLK_PASTE },
	{ KEY_FIND, SDLK_FIND },
	{ KEY_MUTE, SDLK_MUTE },
	{ KEY_VOLUMEUP, SDLK_VOLUMEUP },
	{ KEY_VOLUMEDOWN, SDLK_VOLUMEDOWN },

	{ KEY_ALTERASE, SDLK_ALTERASE },
	{ KEY_SYSREQ, SDLK_SYSREQ },
	{ KEY_CANCEL, SDLK_CANCEL },
	{ KEY_CLEAR, SDLK_CLEAR },
	{ KEY_PRIOR, SDLK_PRIOR },
	{ KEY_RETURN2, SDLK_RETURN2 },
	{ KEY_SEPARATOR, SDLK_SEPARATOR },
	{ KEY_OUT, SDLK_OUT },
	{ KEY_OPER, SDLK_OPER },
	{ KEY_CLEARAGAIN, SDLK_CLEARAGAIN },

	{ KEY_THOUSANDSSEPARATOR, SDLK_THOUSANDSSEPARATOR },
	{ KEY_DECIMALSEPARATOR, SDLK_DECIMALSEPARATOR },
	{ KEY_CURRENCYUNIT, SDLK_CURRENCYUNIT },
	{ KEY_CURRENCYSUBUNIT, SDLK_CURRENCYSUBUNIT },

	{ KEY_LCTRL, SDLK_LCTRL },
	{ KEY_LSHIFT, SDLK_LSHIFT },
	{ KEY_LALT, SDLK_LALT },
	{ KEY_LGUI, SDLK_LGUI },
	{ KEY_RCTRL, SDLK_RCTRL },
	{ KEY_RSHIFT, SDLK_RSHIFT },
	{ KEY_RALT, SDLK_RALT },
	{ KEY_RGUI, SDLK_RGUI },

	{ KEY_MODE, SDLK_MODE },

	{ KEY_AUDIONEXT, SDLK_MEDIA_NEXT_TRACK },
	{ KEY_AUDIOPREV, SDLK_MEDIA_PREVIOUS_TRACK },
	{ KEY_AUDIOSTOP, SDLK_MEDIA_STOP },
	{ KEY_AUDIOPLAY, SDLK_MEDIA_PLAY },
	{ KEY_AUDIOMUTE, SDLK_MUTE },
	{ KEY_MEDIASELECT, SDLK_MEDIA_SELECT },
	{ KEY_APP_SEARCH, SDLK_AC_SEARCH },
	{ KEY_APP_HOME, SDLK_AC_HOME },
	{ KEY_APP_BACK, SDLK_AC_BACK },
	{ KEY_APP_FORWARD, SDLK_AC_FORWARD },
	{ KEY_APP_STOP, SDLK_AC_STOP },
	{ KEY_APP_REFRESH, SDLK_AC_REFRESH },
	{ KEY_APP_BOOKMARKS, SDLK_AC_BOOKMARKS },

	{ KEY_EJECT, SDLK_MEDIA_EJECT },
	{ KEY_SLEEP, SDLK_SLEEP },
};

std::map<SDL_Keycode, Keyboard::Key> Keyboard::sdlKeyToKey;

EnumMap<Keyboard::Scancode, SDL_Scancode, SDL_SCANCODE_COUNT>::Entry Keyboard::scancodeEntries[] =
{
	{SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN},

	{SCANCODE_A, SDL_SCANCODE_A},
	{SCANCODE_B, SDL_SCANCODE_B},
	{SCANCODE_C, SDL_SCANCODE_C},
	{SCANCODE_D, SDL_SCANCODE_D},
	{SCANCODE_E, SDL_SCANCODE_E},
	{SCANCODE_F, SDL_SCANCODE_F},
	{SCANCODE_G, SDL_SCANCODE_G},
	{SCANCODE_H, SDL_SCANCODE_H},
	{SCANCODE_I, SDL_SCANCODE_I},
	{SCANCODE_J, SDL_SCANCODE_J},
	{SCANCODE_K, SDL_SCANCODE_K},
	{SCANCODE_L, SDL_SCANCODE_L},
	{SCANCODE_M, SDL_SCANCODE_M},
	{SCANCODE_N, SDL_SCANCODE_N},
	{SCANCODE_O, SDL_SCANCODE_O},
	{SCANCODE_P, SDL_SCANCODE_P},
	{SCANCODE_Q, SDL_SCANCODE_Q},
	{SCANCODE_R, SDL_SCANCODE_R},
	{SCANCODE_S, SDL_SCANCODE_S},
	{SCANCODE_T, SDL_SCANCODE_T},
	{SCANCODE_U, SDL_SCANCODE_U},
	{SCANCODE_V, SDL_SCANCODE_V},
	{SCANCODE_W, SDL_SCANCODE_W},
	{SCANCODE_X, SDL_SCANCODE_X},
	{SCANCODE_Y, SDL_SCANCODE_Y},
	{SCANCODE_Z, SDL_SCANCODE_Z},

	{SCANCODE_1, SDL_SCANCODE_1},
	{SCANCODE_2, SDL_SCANCODE_2},
	{SCANCODE_3, SDL_SCANCODE_3},
	{SCANCODE_4, SDL_SCANCODE_4},
	{SCANCODE_5, SDL_SCANCODE_5},
	{SCANCODE_6, SDL_SCANCODE_6},
	{SCANCODE_7, SDL_SCANCODE_7},
	{SCANCODE_8, SDL_SCANCODE_8},
	{SCANCODE_9, SDL_SCANCODE_9},
	{SCANCODE_0, SDL_SCANCODE_0},

	{SCANCODE_RETURN, SDL_SCANCODE_RETURN},
	{SCANCODE_ESCAPE, SDL_SCANCODE_ESCAPE},
	{SCANCODE_BACKSPACE, SDL_SCANCODE_BACKSPACE},
	{SCANCODE_TAB, SDL_SCANCODE_TAB},
	{SCANCODE_SPACE, SDL_SCANCODE_SPACE},

	{SCANCODE_MINUS, SDL_SCANCODE_MINUS},
	{SCANCODE_EQUALS, SDL_SCANCODE_EQUALS},
	{SCANCODE_LEFTBRACKET, SDL_SCANCODE_LEFTBRACKET},
	{SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RIGHTBRACKET},
	{SCANCODE_BACKSLASH, SDL_SCANCODE_BACKSLASH},
	{SCANCODE_NONUSHASH, SDL_SCANCODE_NONUSHASH},
	{SCANCODE_SEMICOLON, SDL_SCANCODE_SEMICOLON},
	{SCANCODE_APOSTROPHE, SDL_SCANCODE_APOSTROPHE},
	{SCANCODE_GRAVE, SDL_SCANCODE_GRAVE},
	{SCANCODE_COMMA, SDL_SCANCODE_COMMA},
	{SCANCODE_PERIOD, SDL_SCANCODE_PERIOD},
	{SCANCODE_SLASH, SDL_SCANCODE_SLASH},

	{SCANCODE_CAPSLOCK, SDL_SCANCODE_CAPSLOCK},

	{SCANCODE_F1, SDL_SCANCODE_F1},
	{SCANCODE_F2, SDL_SCANCODE_F2},
	{SCANCODE_F3, SDL_SCANCODE_F3},
	{SCANCODE_F4, SDL_SCANCODE_F4},
	{SCANCODE_F5, SDL_SCANCODE_F5},
	{SCANCODE_F6, SDL_SCANCODE_F6},
	{SCANCODE_F7, SDL_SCANCODE_F7},
	{SCANCODE_F8, SDL_SCANCODE_F8},
	{SCANCODE_F9, SDL_SCANCODE_F9},
	{SCANCODE_F10, SDL_SCANCODE_F10},
	{SCANCODE_F11, SDL_SCANCODE_F11},
	{SCANCODE_F12, SDL_SCANCODE_F12},

	{SCANCODE_PRINTSCREEN, SDL_SCANCODE_PRINTSCREEN},
	{SCANCODE_SCROLLLOCK, SDL_SCANCODE_SCROLLLOCK},
	{SCANCODE_PAUSE, SDL_SCANCODE_PAUSE},
	{SCANCODE_INSERT, SDL_SCANCODE_INSERT},
	{SCANCODE_HOME, SDL_SCANCODE_HOME},
	{SCANCODE_PAGEUP, SDL_SCANCODE_PAGEUP},
	{SCANCODE_DELETE, SDL_SCANCODE_DELETE},
	{SCANCODE_END, SDL_SCANCODE_END},
	{SCANCODE_PAGEDOWN, SDL_SCANCODE_PAGEDOWN},
	{SCANCODE_RIGHT, SDL_SCANCODE_RIGHT},
	{SCANCODE_LEFT, SDL_SCANCODE_LEFT},
	{SCANCODE_DOWN, SDL_SCANCODE_DOWN},
	{SCANCODE_UP, SDL_SCANCODE_UP},

	{SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_NUMLOCKCLEAR},
	{SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_DIVIDE},
	{SCANCODE_KP_MULTIPLY, SDL_SCANCODE_KP_MULTIPLY},
	{SCANCODE_KP_MINUS, SDL_SCANCODE_KP_MINUS},
	{SCANCODE_KP_PLUS, SDL_SCANCODE_KP_PLUS},
	{SCANCODE_KP_ENTER, SDL_SCANCODE_KP_ENTER},
	{SCANCODE_KP_1, SDL_SCANCODE_KP_1},
	{SCANCODE_KP_2, SDL_SCANCODE_KP_2},
	{SCANCODE_KP_3, SDL_SCANCODE_KP_3},
	{SCANCODE_KP_4, SDL_SCANCODE_KP_4},
	{SCANCODE_KP_5, SDL_SCANCODE_KP_5},
	{SCANCODE_KP_6, SDL_SCANCODE_KP_6},
	{SCANCODE_KP_7, SDL_SCANCODE_KP_7},
	{SCANCODE_KP_8, SDL_SCANCODE_KP_8},
	{SCANCODE_KP_9, SDL_SCANCODE_KP_9},
	{SCANCODE_KP_0, SDL_SCANCODE_KP_0},
	{SCANCODE_KP_PERIOD, SDL_SCANCODE_KP_PERIOD},

	{SCANCODE_NONUSBACKSLASH, SDL_SCANCODE_NONUSBACKSLASH},
	{SCANCODE_APPLICATION, SDL_SCANCODE_APPLICATION},
	{SCANCODE_POWER, SDL_SCANCODE_POWER},
	{SCANCODE_KP_EQUALS, SDL_SCANCODE_KP_EQUALS},
	{SCANCODE_F13, SDL_SCANCODE_F13},
	{SCANCODE_F14, SDL_SCANCODE_F14},
	{SCANCODE_F15, SDL_SCANCODE_F15},
	{SCANCODE_F16, SDL_SCANCODE_F16},
	{SCANCODE_F17, SDL_SCANCODE_F17},
	{SCANCODE_F18, SDL_SCANCODE_F18},
	{SCANCODE_F19, SDL_SCANCODE_F19},
	{SCANCODE_F20, SDL_SCANCODE_F20},
	{SCANCODE_F21, SDL_SCANCODE_F21},
	{SCANCODE_F22, SDL_SCANCODE_F22},
	{SCANCODE_F23, SDL_SCANCODE_F23},
	{SCANCODE_F24, SDL_SCANCODE_F24},
	{SCANCODE_EXECUTE, SDL_SCANCODE_EXECUTE},
	{SCANCODE_HELP, SDL_SCANCODE_HELP},
	{SCANCODE_MENU, SDL_SCANCODE_MENU},
	{SCANCODE_SELECT, SDL_SCANCODE_SELECT},
	{SCANCODE_STOP, SDL_SCANCODE_STOP},
	{SCANCODE_AGAIN, SDL_SCANCODE_AGAIN},
	{SCANCODE_UNDO, SDL_SCANCODE_UNDO},
	{SCANCODE_CUT, SDL_SCANCODE_CUT},
	{SCANCODE_COPY, SDL_SCANCODE_COPY},
	{SCANCODE_PASTE, SDL_SCANCODE_PASTE},
	{SCANCODE_FIND, SDL_SCANCODE_FIND},
	{SCANCODE_MUTE, SDL_SCANCODE_MUTE},
	{SCANCODE_VOLUMEUP, SDL_SCANCODE_VOLUMEUP},
	{SCANCODE_VOLUMEDOWN, SDL_SCANCODE_VOLUMEDOWN},
	{SCANCODE_KP_COMMA, SDL_SCANCODE_KP_COMMA},
	{SCANCODE_KP_EQUALSAS400, SDL_SCANCODE_KP_EQUALSAS400},

	{SCANCODE_INTERNATIONAL1, SDL_SCANCODE_INTERNATIONAL1},
	{SCANCODE_INTERNATIONAL2, SDL_SCANCODE_INTERNATIONAL2},
	{SCANCODE_INTERNATIONAL3, SDL_SCANCODE_INTERNATIONAL3},
	{SCANCODE_INTERNATIONAL4, SDL_SCANCODE_INTERNATIONAL4},
	{SCANCODE_INTERNATIONAL5, SDL_SCANCODE_INTERNATIONAL5},
	{SCANCODE_INTERNATIONAL6, SDL_SCANCODE_INTERNATIONAL6},
	{SCANCODE_INTERNATIONAL7, SDL_SCANCODE_INTERNATIONAL7},
	{SCANCODE_INTERNATIONAL8, SDL_SCANCODE_INTERNATIONAL8},
	{SCANCODE_INTERNATIONAL9, SDL_SCANCODE_INTERNATIONAL9},
	{SCANCODE_LANG1, SDL_SCANCODE_LANG1},
	{SCANCODE_LANG2, SDL_SCANCODE_LANG2},
	{SCANCODE_LANG3, SDL_SCANCODE_LANG3},
	{SCANCODE_LANG4, SDL_SCANCODE_LANG4},
	{SCANCODE_LANG5, SDL_SCANCODE_LANG5},
	{SCANCODE_LANG6, SDL_SCANCODE_LANG6},
	{SCANCODE_LANG7, SDL_SCANCODE_LANG7},
	{SCANCODE_LANG8, SDL_SCANCODE_LANG8},
	{SCANCODE_LANG9, SDL_SCANCODE_LANG9},

	{SCANCODE_ALTERASE, SDL_SCANCODE_ALTERASE},
	{SCANCODE_SYSREQ, SDL_SCANCODE_SYSREQ},
	{SCANCODE_CANCEL, SDL_SCANCODE_CANCEL},
	{SCANCODE_CLEAR, SDL_SCANCODE_CLEAR},
	{SCANCODE_PRIOR, SDL_SCANCODE_PRIOR},
	{SCANCODE_RETURN2, SDL_SCANCODE_RETURN2},
	{SCANCODE_SEPARATOR, SDL_SCANCODE_SEPARATOR},
	{SCANCODE_OUT, SDL_SCANCODE_OUT},
	{SCANCODE_OPER, SDL_SCANCODE_OPER},
	{SCANCODE_CLEARAGAIN, SDL_SCANCODE_CLEARAGAIN},
	{SCANCODE_CRSEL, SDL_SCANCODE_CRSEL},
	{SCANCODE_EXSEL, SDL_SCANCODE_EXSEL},

	{SCANCODE_KP_00, SDL_SCANCODE_KP_00},
	{SCANCODE_KP_000, SDL_SCANCODE_KP_000},
	{SCANCODE_THOUSANDSSEPARATOR, SDL_SCANCODE_THOUSANDSSEPARATOR},
	{SCANCODE_DECIMALSEPARATOR, SDL_SCANCODE_DECIMALSEPARATOR},
	{SCANCODE_CURRENCYUNIT, SDL_SCANCODE_CURRENCYUNIT},
	{SCANCODE_CURRENCYSUBUNIT, SDL_SCANCODE_CURRENCYSUBUNIT},
	{SCANCODE_KP_LEFTPAREN, SDL_SCANCODE_KP_LEFTPAREN},
	{SCANCODE_KP_RIGHTPAREN, SDL_SCANCODE_KP_RIGHTPAREN},
	{SCANCODE_KP_LEFTBRACE, SDL_SCANCODE_KP_LEFTBRACE},
	{SCANCODE_KP_RIGHTBRACE, SDL_SCANCODE_KP_RIGHTBRACE},
	{SCANCODE_KP_TAB, SDL_SCANCODE_KP_TAB},
	{SCANCODE_KP_BACKSPACE, SDL_SCANCODE_KP_BACKSPACE},
	{SCANCODE_KP_A, SDL_SCANCODE_KP_A},
	{SCANCODE_KP_B, SDL_SCANCODE_KP_B},
	{SCANCODE_KP_C, SDL_SCANCODE_KP_C},
	{SCANCODE_KP_D, SDL_SCANCODE_KP_D},
	{SCANCODE_KP_E, SDL_SCANCODE_KP_E},
	{SCANCODE_KP_F, SDL_SCANCODE_KP_F},
	{SCANCODE_KP_XOR, SDL_SCANCODE_KP_XOR},
	{SCANCODE_KP_POWER, SDL_SCANCODE_KP_POWER},
	{SCANCODE_KP_PERCENT, SDL_SCANCODE_KP_PERCENT},
	{SCANCODE_KP_LESS, SDL_SCANCODE_KP_LESS},
	{SCANCODE_KP_GREATER, SDL_SCANCODE_KP_GREATER},
	{SCANCODE_KP_AMPERSAND, SDL_SCANCODE_KP_AMPERSAND},
	{SCANCODE_KP_DBLAMPERSAND, SDL_SCANCODE_KP_DBLAMPERSAND},
	{SCANCODE_KP_VERTICALBAR, SDL_SCANCODE_KP_VERTICALBAR},
	{SCANCODE_KP_DBLVERTICALBAR, SDL_SCANCODE_KP_DBLVERTICALBAR},
	{SCANCODE_KP_COLON, SDL_SCANCODE_KP_COLON},
	{SCANCODE_KP_HASH, SDL_SCANCODE_KP_HASH},
	{SCANCODE_KP_SPACE, SDL_SCANCODE_KP_SPACE},
	{SCANCODE_KP_AT, SDL_SCANCODE_KP_AT},
	{SCANCODE_KP_EXCLAM, SDL_SCANCODE_KP_EXCLAM},
	{SCANCODE_KP_MEMSTORE, SDL_SCANCODE_KP_MEMSTORE},
	{SCANCODE_KP_MEMRECALL, SDL_SCANCODE_KP_MEMRECALL},
	{SCANCODE_KP_MEMCLEAR, SDL_SCANCODE_KP_MEMCLEAR},
	{SCANCODE_KP_MEMADD, SDL_SCANCODE_KP_MEMADD},
	{SCANCODE_KP_MEMSUBTRACT, SDL_SCANCODE_KP_MEMSUBTRACT},
	{SCANCODE_KP_MEMMULTIPLY, SDL_SCANCODE_KP_MEMMULTIPLY},
	{SCANCODE_KP_MEMDIVIDE, SDL_SCANCODE_KP_MEMDIVIDE},
	{SCANCODE_KP_PLUSMINUS, SDL_SCANCODE_KP_PLUSMINUS},
	{SCANCODE_KP_CLEAR, SDL_SCANCODE_KP_CLEAR},
	{SCANCODE_KP_CLEARENTRY, SDL_SCANCODE_KP_CLEARENTRY},
	{SCANCODE_KP_BINARY, SDL_SCANCODE_KP_BINARY},
	{SCANCODE_KP_OCTAL, SDL_SCANCODE_KP_OCTAL},
	{SCANCODE_KP_DECIMAL, SDL_SCANCODE_KP_DECIMAL},
	{SCANCODE_KP_HEXADECIMAL, SDL_SCANCODE_KP_HEXADECIMAL},

	{SCANCODE_LCTRL, SDL_SCANCODE_LCTRL},
	{SCANCODE_LSHIFT, SDL_SCANCODE_LSHIFT},
	{SCANCODE_LALT, SDL_SCANCODE_LALT},
	{SCANCODE_LGUI, SDL_SCANCODE_LGUI},
	{SCANCODE_RCTRL, SDL_SCANCODE_RCTRL},
	{SCANCODE_RSHIFT, SDL_SCANCODE_RSHIFT},
	{SCANCODE_RALT, SDL_SCANCODE_RALT},
	{SCANCODE_RGUI, SDL_SCANCODE_RGUI},

	{SCANCODE_MODE, SDL_SCANCODE_MODE},

	{SCANCODE_AUDIONEXT, SDL_SCANCODE_MEDIA_NEXT_TRACK},
	{SCANCODE_AUDIOPREV, SDL_SCANCODE_MEDIA_PREVIOUS_TRACK},
	{SCANCODE_AUDIOSTOP, SDL_SCANCODE_MEDIA_STOP},
	{SCANCODE_AUDIOPLAY, SDL_SCANCODE_MEDIA_PLAY},
	{SCANCODE_AUDIOMUTE, SDL_SCANCODE_MUTE},
	{SCANCODE_MEDIASELECT, SDL_SCANCODE_MEDIA_SELECT},
	{SCANCODE_AC_SEARCH, SDL_SCANCODE_AC_SEARCH},
	{SCANCODE_AC_HOME, SDL_SCANCODE_AC_HOME},
	{SCANCODE_AC_BACK, SDL_SCANCODE_AC_BACK},
	{SCANCODE_AC_FORWARD, SDL_SCANCODE_AC_FORWARD},
	{SCANCODE_AC_STOP, SDL_SCANCODE_AC_STOP},
	{SCANCODE_AC_REFRESH, SDL_SCANCODE_AC_REFRESH},
	{SCANCODE_AC_BOOKMARKS, SDL_SCANCODE_AC_BOOKMARKS},

	{SCANCODE_EJECT, SDL_SCANCODE_MEDIA_EJECT},
	{SCANCODE_SLEEP, SDL_SCANCODE_SLEEP},
};

EnumMap<Keyboard::Scancode, SDL_Scancode, SDL_SCANCODE_COUNT> Keyboard::scancodes(Keyboard::scancodeEntries, sizeof(Keyboard::scancodeEntries));

} // sdl
} // keyboard
} // love
